/**
* @file sample_process.cpp
*
* Copyright (C) 2020. Huawei Technologies Co., Ltd. All rights reserved.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "sample_process.h"
#include <iostream>
#include <pthread.h>
#include "model_process.h"
#include "acl/acl.h"
#include "utils.h"
using namespace std;
extern bool g_isDevice;

SampleProcess::SampleProcess() :deviceId_(0), context_(nullptr), stream_(nullptr)
{
}

SampleProcess::~SampleProcess()
{
    DestroyResource();
}

Result SampleProcess::InitResource()
{
    // ACL init
    const char *aclConfigPath = "test_data/config/acl.json";
    aclError ret = aclInit(aclConfigPath);
    if (ret != ACL_ERROR_NONE) {
        ERROR_LOG("acl init failed, errorCode = %d", static_cast<int32_t>(ret));
        return FAILED;
    }
    INFO_LOG("acl init success");

    // set device
    ret = aclrtSetDevice(deviceId_);
    if (ret != ACL_ERROR_NONE) {
        ERROR_LOG("acl set device %d failed, errorCode = %d", deviceId_, static_cast<int32_t>(ret));
        return FAILED;
    }
    INFO_LOG("set device %d success", deviceId_);
    return SUCCESS;
}
Result SampleProcess::CreatContext() 
{
    // create context (set current)
    aclError ret = aclrtCreateContext(&context_, deviceId_);
    if (ret != ACL_ERROR_NONE) {
        ERROR_LOG("acl create context failed, deviceId = %d, errorCode = %d",
            deviceId_, static_cast<int32_t>(ret));
        return FAILED;
    }
    INFO_LOG("create context success");

    // create stream
    ret = aclrtCreateStream(&stream_);
    if (ret != ACL_ERROR_NONE) {
        ERROR_LOG("acl create stream failed, deviceId = %d, errorCode = %d",
            deviceId_, static_cast<int32_t>(ret));
        return FAILED;
    }
    INFO_LOG("create stream success");

    // get run mode
    // runMode is ACL_HOST which represents app is running in host
    // runMode is ACL_DEVICE which represents app is running in device
    aclrtRunMode runMode;
    ret = aclrtGetRunMode(&runMode);
    if (ret != ACL_ERROR_NONE) {
        ERROR_LOG("acl get run mode failed, errorCode = %d", static_cast<int32_t>(ret));
        return FAILED;
    }
    g_isDevice = (runMode == ACL_DEVICE);
    INFO_LOG("get run mode success");
    return SUCCESS;
}

Result SampleProcess::Process()
{
    // model init
    Result ret = CreatContext();
    if (ret != SUCCESS) {
        ERROR_LOG("execute creatcontext failed");
        return FAILED;
    }
    ModelProcess modelProcess;
    const char* omModelPath = "op_models/0_ReshapeCust_3_2_9_3_2_2_3_2_3_3.om";
    ret = modelProcess.LoadModel(omModelPath);
    if (ret != SUCCESS) {
        ERROR_LOG("execute LoadModel failed");
        return FAILED;
    }
   
    ret = modelProcess.CreateModelDesc();
    if (ret != SUCCESS) {
        ERROR_LOG("execute CreateModelDesc failed");
        return FAILED;
    }

    string testFile[] = {
        "test_data/data/input_0.bin",
        "test_data/data/input_1.bin"
    };
    
    void *picDevBuffer[2] = { nullptr, nullptr };
    for (size_t index = 0; index < 2; ++index) {
        INFO_LOG("start to process file:%s", testFile[index].c_str());
        size_t devBufferSize;
        
        ret = modelProcess.GetInputSizeByIndex(index, devBufferSize);
        if (ret != SUCCESS) {
            ERROR_LOG("execute GetInputSizeByIndex failed");
            return FAILED;
        }
        aclError aclRet = aclrtMallocCached(&picDevBuffer[index], devBufferSize, ACL_MEM_MALLOC_NORMAL_ONLY);
        if (aclRet != ACL_ERROR_NONE) {
            ERROR_LOG("malloc device buffer failed. size is %zu, errorCode is %d",
                devBufferSize, static_cast<int32_t>(aclRet));
            return FAILED;
        }
         // copy image data to device buffer
        ret = Utils::MemcpyFileToDeviceBuffer(testFile[index], picDevBuffer[index], devBufferSize);
        if (ret != SUCCESS) {
            aclrtFree(picDevBuffer[index]);
            ERROR_LOG("memcpy device buffer failed, index is %zu", index);
            return FAILED;
        }

        ret = modelProcess.CreateInput(picDevBuffer[index], devBufferSize, index);
        if (ret != SUCCESS) {
            ERROR_LOG("execute CreateInput failed");
            aclrtFree(picDevBuffer[index]);
            return FAILED;
        }
    }

    ret = modelProcess.CreateOutput();
    if (ret != SUCCESS) {
        aclrtFree(picDevBuffer[0]);
        aclrtFree(picDevBuffer[1]);
        ERROR_LOG("execute CreateOutput failed");
        return FAILED;
    }

    ret = modelProcess.Execute();
    if (ret != SUCCESS) {
        ERROR_LOG("execute inference failed");
        return FAILED;
    }

    // release model input output
    modelProcess.DumpModelOutputResult();
    modelProcess.OutputModelResult();
    aclrtFree(picDevBuffer[0]);
    aclrtFree(picDevBuffer[1]);
    modelProcess.DestroyInput();
    modelProcess.DestroyOutput();
    aclError ret1 = aclrtResetDevice(deviceId_);
    if (ret1 != ACL_ERROR_NONE) {
        ERROR_LOG("reset device %d failed, errorCode = %d", deviceId_, static_cast<int32_t>(ret1));
    }
    INFO_LOG("end to reset device %d", deviceId_);
    return SUCCESS;
}

void SampleProcess::DestroyResource()
{
    aclError ret;
    if (stream_ != nullptr) {
        ret = aclrtDestroyStream(stream_);
        if (ret != ACL_ERROR_NONE) {
            ERROR_LOG("destroy stream failed, errorCode = %d", static_cast<int32_t>(ret));
        }
        stream_ = nullptr;
    }
    INFO_LOG("end to destroy stream");

    if (context_ != nullptr) {
        ret = aclrtDestroyContext(context_);
        if (ret != ACL_ERROR_NONE) {
            ERROR_LOG("destroy context failed, errorCode = %d", static_cast<int32_t>(ret));
        }
        context_ = nullptr;
    }
    INFO_LOG("end to destroy context");

    ret = aclFinalize();
    if (ret != ACL_ERROR_NONE) {
        ERROR_LOG("finalize acl failed, errorCode = %d", static_cast<int32_t>(ret));
    }
    INFO_LOG("end to finalize acl");
}
